/*
 *   This file is part of ed_pvr.
 *
 *   ed_pvr is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   ed_pvr is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with ed_pvr.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
#include <getopt.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/shm.h>
#include <malloc.h>
#include "device.h"
#include "utils.h"
#include "tuner_restream.h"
#include "mpegts_io.h"
#include "protocol.h"
#include "timers.h"
#include "xmltv.h"
#include "zap2it.h"
#include "win.h"

#define RESTREAM_DEFAULT_PAD_TIME				(5 * 60) // in seconds
#define RESTREAM_MAX_DEV_COUNT					12
//#define RESTREAM_DEFAULT_READ_SZ				(170 * TS_PACKET_SIZE)
#define RESTREAM_DEFAULT_READ_SZ				(90 * TS_PACKET_SIZE)
#define RESTREAM_DEFAULT_BINDIP					"0.0.0.0"
#define RESTREAM_DEFAULT_BINDPORT				8991

#define RESTREAM_DEFAULT_CHANNELS_CONF			"chanmap.txt"
#define RESTREAM_TIMER_FILENAME					"restream.timers"

#define RESTREAM_HTTP_OK_STR 					"HTTP/1.0 200 OK"

// Callback(s) for device

uint8_t restream_read_data(TUNER_STRUCT *);
int restream_tune_channel(TUNER_STRUCT *, TUNER_INITPASS_STRUCT *);

// Internal functions, shouldn't be called from outside
XMLTV_STRUCT * _restream_get_xmltv(TUNER_RESTREAM_STRUCT *);
int _restream_get_dd_progid(TUNER_RESTREAM_STRUCT *, TIMER_STRUCT *);
int _restream_get_channel_lineup(TUNER_RESTREAM_STRUCT *);
int _restream_get_channel_names_from_xmltv(TUNER_RESTREAM_STRUCT *);
int _restream_http_connect(TUNER_CHANNEL_STRUCT *, TUNER_RESTREAM_STRUCT *);
uint8_t restream_destroy(TUNER_STRUCT *);

// Internal function for timer
void _restream_chan_group_print_all(TUNER_RESTREAM_STRUCT *);
int _restream_chan_group_add(TUNER_RESTREAM_STRUCT *, char *, uint8_t, uint8_t);
RESTREAM_CHANNEL_GROUP *_restream_chan_group_get_struct(TUNER_RESTREAM_STRUCT *, char *);
RESTREAM_CHANNEL_URL_LIST * _restream_build_tuner_list(TUNER_RESTREAM_STRUCT *);
int _restream_find_conflicts(TIMER_STRUCT *, TIMER_STRUCT *);
void _restream_url_list_print(RESTREAM_URL *);
void _restream_tunerlist_print(RESTREAM_CHANNEL_URL_LIST *);
int _restream_tunerlist_mark_used(RESTREAM_CHANNEL_URL_LIST *, char *);

//int _restream_timer_stop(TUNER_INITPASS_STRUCT *);
int _restream_timer_list(TUNER_INITPASS_STRUCT *);
int _restream_timer_del(TUNER_INITPASS_STRUCT *);
int _restream_timer_add(TUNER_INITPASS_STRUCT *);
int _restream_timer_count(TUNER_INITPASS_STRUCT *);
int _restream_timer_seqnum(TUNER_INITPASS_STRUCT *);

int _restream_get_chan_epg(TUNER_INITPASS_STRUCT *);
int _restream_dev_wait(TUNER_INITPASS_STRUCT *);

// Internal function for recording
//int _restream_generate_rec_file(char *, TIMER_STRUCT *);

int _restream_reload_recordings(TUNER_RESTREAM_STRUCT *);
char * _restream_getline_from_rec(UTILS_DATAPAIR_STRUCT *, uint8_t);
int _restream_rec_count(TUNER_INITPASS_STRUCT *);
int _restream_rec_list(TUNER_INITPASS_STRUCT *);
int _restream_rec_play(TUNER_INITPASS_STRUCT *);
int _restream_rec_del(TUNER_INITPASS_STRUCT *);
int _restream_rec_stop(TUNER_INITPASS_STRUCT *);
int _restream_rec_seqnum(TUNER_INITPASS_STRUCT *);
int _restream_reload_epg(TUNER_INITPASS_STRUCT *);
void _restream_stop_recording(TIMER_STRUCT *);
//void _restream_do_recording(TIMER_STRUCT *);

int _restream_tuner_internal_init(TUNER_STRUCT *, DEVICE_INIT_ARGS_STRUCT *);
int _restream_fill_timer_xmltv(TUNER_RESTREAM_STRUCT *, TIMER_STRUCT *);

zap2it_epg_s * _restream_get_zap2it(TUNER_RESTREAM_STRUCT *);

/*****************************************************************/


MPEGTS_PAT_PROGRAM_STRUCT * _restream_process_pat(MPEGTS_PAT * pat, TUNER_CHANNEL_STRUCT * tune_chan)
{
	if (pat && pat->program_count == 1 && pat->first_program)
	{
		MPEGTS_PAT_PROGRAM_STRUCT * n = malloc(sizeof(MPEGTS_PAT_PROGRAM_STRUCT));
		memcpy(n, pat->first_program, sizeof(MPEGTS_PAT_PROGRAM_STRUCT));
		return n;
	}
	return NULL;
}


char * _restream_getline_from_rec(UTILS_DATAPAIR_STRUCT *data, uint8_t lineno)
{
	char * ret = NULL;

	if (data && data->data)
	{
		char * tmpmem = malloc(data->data_size);

		if (tmpmem)
		{
			int i=0, j=0;
			char *lines[255] = {0};
			memcpy(tmpmem, data->data, data->data_size);
			lines[j++] = tmpmem;

			for(i=0; i < data->data_size; i++)
			{
				if (tmpmem[i] == '\n')
				{
					tmpmem[i] = '\0';
					if (i + 1 <= data->data_size)
						lines[j++] = &tmpmem[i+1];
				}
			}

			for(i=0; i < j; i++)
			{
				if (i == lineno)
				{
				 	if (lines[i])
						ret = strdup(lines[i]);
					break;
				}
			}
			free(tmpmem);
		}

	}
	return ret;
}

int _restream_reload_recordings(TUNER_RESTREAM_STRUCT *dev)
{
	if (dev)
	{
//		uint8_t id = 1;
		UTILS_DATAPAIR_STRUCT *rec_data = NULL;
		MPEGTS_IO_STRUCT * tmpio = NULL;

		if (!dev->recording_cleanup)
		{
			//utils_log_print(dev->device->log, "[RESTREAM] Cleaning rec. dir\n");
			//mpegts_io_cleanup_recordings(dev->recording_dir);
			dev->recording_cleanup = 1;

		}

		pthread_mutex_lock(&dev->recording_mutex);
//		utils_log_print(dev->device->log, "[RESTREAM] Reloading recordings...\n");

//		printf("R1\n");
		dev->recordings = mpegts_io_load_recordings(dev->recording_dir, dev->recordings);
//		printf("R2\n");
//		utils_log_print(dev->device->log, "[RESTREAM] Reloading recordings 1\n");
		if (!dev->recordings)
		{
			utils_log_print(dev->device->log, "[RESTREAM] sad1..\n");
		}
		tmpio = dev->recordings->first_io_struct;

//		utils_log_print(dev->device->log, "[RESTREAM] Reloading recordings 3\n");
		while(tmpio)
		{
			rec_data = mpegts_io_get_rec_data(tmpio);

			if (rec_data)
			{
				char * tmp = NULL;
				TIMER_STRUCT * timer = NULL;

//				if (tmpio->extra_data[1])
//					printf("UGGGGGGGGGGGGGGGH\n");

				if (!tmpio->extra_data[1])
				{
					tmpio->extra_data[1] = calloc(1, sizeof(UTILS_DATAPAIR_STRUCT));
					tmpio->extra_data[1]->data_size = sizeof(TIMER_STRUCT);
					tmpio->extra_data[1]->data = calloc(1, tmpio->extra_data[1]->data_size);
				}

				timer = (TIMER_STRUCT *)tmpio->extra_data[1]->data;

				if (timer)
				{
					timer->data.id = tmpio->id;
//					utils_log_print(dev->device->log, "id: %d -- %p\n", timer->data.id, tmpio);
					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_CHANNEL_NUM);
					if (tmp)
					{
						timer->data.channel_num = strtol(tmp, (char **)NULL, 10);
//						utils_log_print(dev->device->log, "ChanNum: %" PRId64"\n", timer->data.channel_num);
						free(tmp);
					}

					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_START_TIME);
					if (tmp)
					{
						timer->data.start_epoch = strtol(tmp, (char **)NULL, 10);
//						utils_log_print(dev->device->log, "S_epoch: %" PRId64"\n", timer->data.start_epoch);
						free(tmp);
					}

					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_END_TIME);
					if (tmp)
					{
						timer->data.end_epoch = strtol(tmp, (char **)NULL, 10);
//						utils_log_print(dev->device->log, "e_epoch: %" PRId64"\n", timer->data.end_epoch);
						free(tmp);
					}


					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_CHANNEL_NAME);
					if (tmp)
					{
						strncpy(timer->data.channel_name, tmp, sizeof(timer->data.channel_name));
//						utils_log_print(dev->device->log, "ChName: %s\n", timer->data.channel_name);
						free(tmp);
					}

					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_TITLE);
					if (tmp)
					{
						strncpy(timer->data.title, tmp, sizeof(timer->data.title));
//						printf("Title: %s\n", timer->data.title);
						free(tmp);
					}

					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_PLOT);
					if (tmp)
					{
//						printf("\t\t%d -- %ld\n", sizeof(), sizeof(timer->data.plot));
						strncpy(timer->data.plot, tmp, sizeof(timer->data.plot));
//						printf("Plot: %s\n", timer->data.plot);
						free(tmp);
					}
					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_PLOT_OUTLINE);
					if (tmp)
					{
						strncpy(timer->data.plot_outline, tmp, sizeof(timer->data.plot_outline));
//						printf("Plot Out: %s\n", timer->data.plot_outline);
						free(tmp);
					}
					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_GENRE);
					if (tmp)
					{
						strncpy(timer->data.genre, tmp, sizeof(timer->data.genre));
//						printf("Genre: %s\n", timer->data.genre);
						free(tmp);
					}
					tmp = _restream_getline_from_rec(rec_data, RESTREAM_REC_SUBGENRE);
					if (tmp)
					{
						strncpy(timer->data.sub_genre, tmp, sizeof(timer->data.sub_genre));
//						printf("Sub_Genre: %s\n", timer->data.sub_genre);
						free(tmp);
					}
//					utils_log_print(dev->device->log, "[RESTREAM] Loaded recoding, channel: %" PRId64" -- %s\n", timer->data.channel_num, timer->data.title);
				}
			}
			else
			{
				utils_log_print(dev->device->log, "------NO REC DATA?\n");
			}
			tmpio = tmpio->next;
		}

		pthread_mutex_unlock(&dev->recording_mutex);
//		utils_log_print(restream_dev->device->log("R1\n");
//		utils_log_print(dev->device->log,"[RESTREAM] Recs have reloaded\n");
		return 0;
	}
	else
	{
//		utils_log_print(dev->device->log, "BLEAH\n");
	}
	return -1;
}

void _restream_stop_recording(TIMER_STRUCT * timer)
{
	if (timer && timer->parent && timer->parent->extra_data)
	{
//		utils_log_print(restream_dev->device->log("R1\n");
		TIMER_CONTROL_STRUCT *control = timer->parent;
		TUNER_STRUCT *tmptuner = (TUNER_STRUCT *)timer->extra_data;
		TUNER_RESTREAM_STRUCT *restream_dev = (TUNER_RESTREAM_STRUCT *)control->extra_data;
//		DEVICE_STRUCT *dev = tmptuner->parent;

//		utils_log_print(restream_dev->device->log("R2\n");
		if (tmptuner)
		{
//		utils_log_print(restream_dev->device->log("R3\n");

			tmptuner->writing = 0;
//				usleep(1000*3000);
//		utils_log_print(restream_dev->device->log("R4\n");

			while(tmptuner->tuned_channel)
			{
				if (tmptuner)
				{
//					utils_log_print(restream_dev->device->log, "_restream_stop_recording 1: %d (id: %d)\n", tmptuner->tuned_channel, tmptuner->id);
				}
				else
				{
					utils_log_print(restream_dev->device->log, "wtf??\n");
				}
				usleep(1000*2000);
			}
//			utils_log_print(restream_dev->device->log, "_restream_stop_recording 2\n");
			tmptuner->alive = 0;

//			utils_log_print(restream_dev->device->log, "_restream_stop_recording 3\n");
			close(tmptuner->devhandle);
			tmptuner->devhandle = 0;
//			utils_log_print(restream_dev->device->log, "_restream_stop_recording 4\n");

			while(tmptuner->read_thread)
			{
//				utils_log_print(restream_dev->device->log, "_restream_stop_recording 5\n");
				usleep(1000*250);
			}
//			_restream_generate_rec_file(tmptuner->io_opts.filename_base, timer);

			if (tmptuner->parent)
				free(tmptuner->parent);

			if (restream_dev)
			{
//				_restream_reload_recordings(restream_dev);
			}
			else
				utils_log_print(restream_dev->device->log, "restream is null :(\n");

			free(tmptuner);

		}
		else
		{
//			printf("_restream_stop_recording HAZ A SADZ\n");
		}
	}
}

void _restream_url_list_print(RESTREAM_URL *url_list)
{
//	if (url_list)
//		utils_log_print(dev->device->log, "%s [used: %d]\n", url_list->tuner_url, url_list->used);
}

void _restream_tunerlist_print(RESTREAM_CHANNEL_URL_LIST *list)
{
	if (list)
	{
		int i;
		for(i=0; i < list->count; i++)
		{
//			utils_log_print(dev->device->log, "URL_LIST [%d] -- ", i);
			_restream_url_list_print(&list->list[i]);
		}
	}
}
void _restream_chan_group_print_all(TUNER_RESTREAM_STRUCT *dev)
{
	if (dev)
	{
		int i;
		for (i=0; i < RESTREAM_MAX_CHAN_GROUPS; i++)
		{
			if (dev->tun_chan_group[i].tuner_count)
			{
				utils_log_print(dev->device->log, "[RESTREAM] chan_group (%d), url: %s tuner count: %d\n", i, dev->tun_chan_group[i].base_url, dev->tun_chan_group[i].tuner_count);
			}
		}
	}
}

int _restream_tunerlist_mark_used(RESTREAM_CHANNEL_URL_LIST *list, char *url)
{
	if (list)
	{
		int i;
		for(i=0; i < list->count; i++)
		{
			if (!list->list[i].used && strncmp(list->list[i].tuner_url, url, 4095) == 0)
			{
				list->list[i].used = 1;
				return 1;
			}
		}
	}
	return 0;
}

int _restream_tunerlist_find_free(RESTREAM_CHANNEL_URL_LIST *list, TUNER_CHANNEL_STRUCT * tuner_channel)
{
	if (list && tuner_channel)
	{
		int i, j;
		for(j=0; j < tuner_channel->url_count; j++)
		{
			for(i=0; i < list->count; i++)
			{
//				printf("I [%d]: %d\n", list->count, i);
				if (!list->list[i].used && strncmp(list->list[i].tuner_url, tuner_channel->urls[j], 4095) == 0)
				{
					return i;
				}
			}
		}
	}
	return -1;
}

RESTREAM_CHANNEL_GROUP *_restream_chan_group_get_struct(TUNER_RESTREAM_STRUCT *dev, char * url)
{
	if (dev)
	{
		int i;
		for (i=0; i < RESTREAM_MAX_CHAN_GROUPS; i++)
		{
			if (strncmp(dev->tun_chan_group[i].base_url, url, 4095) == 0)
			{
//				printf("[RESTREAM] chan_group (%d), url: %s\n", i, dev->tun_chan_group[i].base_url);
				return  &dev->tun_chan_group[i];
			}
		}
	}
	return NULL;
}

int _restream_chan_group_add(TUNER_RESTREAM_STRUCT *dev, char * url, uint8_t tuner_count, uint8_t type)
{
	if (dev && url && tuner_count)
	{
		int i;

		for (i=0; i < RESTREAM_MAX_CHAN_GROUPS; i++)
		{
			if (strncmp(dev->tun_chan_group[i].base_url, url, 4095) == 0)
				break;
		}

		if (i >= RESTREAM_MAX_CHAN_GROUPS)
		{
//			int q;

			dev->tun_chan_group[dev->tun_chan_group_count].type = type;
			strncpy(dev->tun_chan_group[dev->tun_chan_group_count].base_url, url, 4095);
			dev->tun_chan_group[dev->tun_chan_group_count].tuner_count = tuner_count;
			dev->tun_chan_group_count++;
			return 0;
		}
	}
	return 1;
}


//RESTREAM_CHANNEL_URL_LIST * _restream_build_tuner_list(TUNER_RESTREAM_STRUCT *dev, TUNER_CHANNEL_STRUCT * tuner_channel)
RESTREAM_CHANNEL_URL_LIST * _restream_build_tuner_list(TUNER_RESTREAM_STRUCT *dev)
{
	int i;
	RESTREAM_CHANNEL_URL_LIST * ret = dev->url_list;

	if (dev && !ret)
	{
		RESTREAM_CHANNEL_GROUP * chan_grp = NULL;
		dev->url_list = ret = calloc(1, sizeof(RESTREAM_CHANNEL_URL_LIST));

		for (i = 0; i < dev->tun_chan_group_count; i++)
		{
			chan_grp = &dev->tun_chan_group[i];
			if (chan_grp)
			{
				int q;
				for(q=0; q < chan_grp->tuner_count; q++)
				{
					//ret->tuner_list[ret->count++] = strdup(chan_grp->base_url);
					ret->list[ret->count].tuner_url = strdup(chan_grp->base_url);
//					printf("_restream_build_tuner_list: %s\n", ret->list[ret->count].tuner_url);
					ret->list[ret->count++].used = 0;
				}
			}
		}
	}
	else if (ret)
	{
		for(i = 0; i < 255; i++)
			ret->list[i].used = 0;
	}
	return ret;
}

int _restream_find_conflicts(TIMER_STRUCT *timer_data1, TIMER_STRUCT *timer_data2)
{
	if (timer_data1 && timer_data2)
	{
//		int conflict_count = 0;

		if (timer_data1->data.start_epoch >= timer_data2->data.start_epoch && timer_data1->data.start_epoch <= timer_data2->data.end_epoch)
			return 1;

		if (timer_data1->data.end_epoch >= timer_data2->data.start_epoch && timer_data1->data.end_epoch <= timer_data2->data.end_epoch)
			return 2;

		if (timer_data2->data.start_epoch >= timer_data1->data.start_epoch && timer_data2->data.start_epoch <= timer_data1->data.end_epoch)
			return 3;

		if (timer_data2->data.end_epoch >= timer_data1->data.start_epoch && timer_data2->data.end_epoch <= timer_data1->data.end_epoch)
			return 4;

		return 0;
	}
	return -1;
}

RESTREAM_CHANNEL_GROUP * _restream_find_tuner(TUNER_RESTREAM_STRUCT *dev, TIMER_STRUCT *timer_data, RESTREAM_CHANNEL_URL_LIST * url_list)
{
//	printf("_restream_find_tuner start\n");
	if (dev && dev->timer_ctl && timer_data && url_list)
	{
		char tmp_url[4097] = {0};
		TUNER_CHANNEL_STRUCT * tuner_channel = device_channel_get_struct(dev->device, timer_data->data.channel_num, timer_data->data.channel_num);
//	printf("_restream_find_tuner 1: %" PRId64"\n", timer_data->data.channel_num);
		if (tuner_channel)
		{
			int count = 0, tmp;
			uint8_t conflict_count = 0;
//	printf("_restream_find_tuner 2\n");

			TIMER_STRUCT *timer_list_first = timer_list_timer(dev->timer_ctl);
			TIMER_STRUCT *timer_list = timer_list_first;

			snprintf(tmp_url, 4096, "127.0.0.1:%d", dev->device->bind_port);

			while (timer_list)
			{
				count++;
				tmp = _restream_find_conflicts(timer_data, timer_list);

				if (tmp > 0)
				{
					conflict_count++;
//					printf("Conflict: %d -- %s [%" PRId64" : %" PRId64"]\n", tmp, timer_list->data.url, timer_list->data.channel_num , timer_data->data.channel_num);
					if (timer_list->data.channel_num != timer_data->data.channel_num)
						_restream_tunerlist_mark_used(url_list, timer_list->data.url);
//					timer_print_timer(timer_list);
//					timer_print_timer(timer_data);
				}
				else if (tmp < 0)
				{
					utils_log_print(dev->device->log, "Shouldn't happen\n");
					// Shouldn't happen, 
				}
				timer_list = timer_list->next;
			}
			printf("Conflict count: %d\n", conflict_count);
//			_restream_tunerlist_print(url_list);
//			printf("old url: %s, new: %s\n", tuner_channel->urls[0], tmp_url);
			if (conflict_count == 0)
			{
//				int i;
/*
				strncpy(timer_data->data.url, tuner_channel->urls[0], 4095);
				for(tmp=0; tmp <  tuner_channel->url_count; tmp++)
					strncpy(timer_data->backup_urls[tmp], tuner_channel->urls[tmp], 4095);
*/
				strncpy(timer_data->data.url, tmp_url, 4095);
				return _restream_chan_group_get_struct(dev, tuner_channel->urls[0]);
			}
			else
			{
				if (conflict_count)
				{
					int i = _restream_tunerlist_find_free(url_list, tuner_channel);
					if (i >= 0)
					{
//						printf("Found free at index: %d (%s)\n", i, url_list->list[i].tuner_url);
/*
						strncpy(timer_data->data.url, url_list->list[i].tuner_url, 4095);
						for(tmp=0; tmp <  tuner_channel->url_count; tmp++)
							strncpy(timer_data->backup_urls[tmp], tuner_channel->urls[tmp], 4095);
*/
						strncpy(timer_data->data.url, tmp_url, 4095);
						return _restream_chan_group_get_struct(dev, url_list->list[i].tuner_url);
					}
				}
			}
		}
	}
	printf("_restream_find_tuner end\n");

	return NULL;
}

uint64_t _restream_extract_channelnum(char *id)
{
	uint64_t ret = 0;
	if (id)
	{
		int i, k=0;
		char tmp[64] = {0};

		for(i = 0; i < strlen(id); i++)
		{
			if (isdigit(id[i]))
				tmp[k++] = id[i];
			else if (id[i] == '.')
				break;
		}
		if (k)
			ret = strtol(tmp, (char **)NULL, 10);
	}

	return ret;
}

zap2it_epg_s * _restream_get_zap2it(TUNER_RESTREAM_STRUCT * restream_dev)
{
	zap2it_epg_s *epg = NULL;
	if (restream_dev)
	{
		pthread_mutex_lock(&restream_dev->xmltv_mutex);
		if (!restream_dev->zap2it_epg)
		{
			restream_dev->zap2it_epg = epg = zap2it_epg_init(8, 1);
			if (epg)
			{
				epg->cache = 1;
				epg->fetch_days = ZAP2IT_MAX_EPG_DAYS;
				strncpy(epg->zap_lineup, restream_dev->zap2it_lineup, sizeof(epg->zap_lineup) - 1);
				strncpy(epg->zap_zip, restream_dev->zap2it_zipcode, sizeof(epg->zap_zip) - 1);
				if (restream_dev->zap2it_cachedir)
				{
					strncpy(epg->cache_dir, restream_dev->zap2it_cachedir, sizeof(epg->cache_dir) - 1);
					mkdir(epg->cache_dir, S_IRWXU | S_IRWXG | S_IRWXO);
					printf("[RESTREAM] Zap2it cachedir: %s\n", epg->cache_dir);
				}
				else
				{
					strncpy(epg->cache_dir, "./", sizeof(epg->cache_dir) - 1);
				}
				epg->start_time = zap_get_start_time(time(NULL));

			}
		}
		else
		{
			epg = restream_dev->zap2it_epg;
		}
		pthread_mutex_unlock(&restream_dev->xmltv_mutex);
	}
	return epg;
}

XMLTV_STRUCT * _restream_get_xmltv(TUNER_RESTREAM_STRUCT * restream_dev)
{
	XMLTV_STRUCT * ret = NULL;
	if (restream_dev)
	{
		pthread_mutex_lock(&restream_dev->xmltv_mutex);
		if (!restream_dev->xmltv_cache)
		{
			printf("[RESTREAM] [XMLTV] - Loading: %s ..\n", restream_dev->xmltv_file);
			ret = xmltv_init(restream_dev->xmltv_file);
			if (ret)
			{
				restream_dev->xmltv_cache = ret;
				printf("[RESTREAM] [XMLTV] - Loading done good\n");
			}
			else
				printf("[RESTREAM] [XMLTV] - Loading done NOT good\n");

//			printf("[RESTREAM] xMLTV - Loading done, caching channel names\n");
//			_restream_get_channel_names_from_xmltv(restream_dev);
//			printf("[RESTREAM] XMLTV all done!\n");
			//restream_dev->xmltv_cache->cached = 1;
		}
		else
		{
			ret = restream_dev->xmltv_cache;
		}
		pthread_mutex_unlock(&restream_dev->xmltv_mutex);
		
	}
	return ret;
}

int _restream_fill_timer_xmltv(TUNER_RESTREAM_STRUCT * restream_dev, TIMER_STRUCT *timer)
{
//	if (restream_dev && timer && restream_dev->xmltv_file && file_exists(restream_dev->xmltv_file) == 0)
	if (restream_dev && timer && restream_dev->xmltv_file)
	{
//		XMLTV_STRUCT * x = xmltv_init(restream_dev->xmltv_file);
		char * dd_progid = strdup(timer->data.title);
		XMLTV_STRUCT * x = _restream_get_xmltv(restream_dev);

		if (x && dd_progid)
		{
			xmlChar *start=NULL, *end=NULL;
			start = (xmlChar *)utils_epoch_to_str(timer->data.start_epoch);
			end = (xmlChar *)utils_epoch_to_str(timer->data.end_epoch);


			utils_log_print(restream_dev->device->log,  "checking xml (%s) for progid: %s\n", restream_dev->xmltv_file, dd_progid);
//			xmlNodePtr ptr = xmltv_get_episode_dd_nodeptr(x, dd_progid);
			xmlNodePtr ptr = xmltv_get_id_and_times(x, timer->data.url, (char*)start, (char*)end);

			if (ptr)
			{
				xmlChar *s = NULL, *e = NULL, *c = NULL;
				xmlChar *title = NULL, *chname = NULL, *stitle = NULL, *cat = NULL, *desc = NULL;
				s =  xmlGetProp(ptr, (xmlChar*)"start");
				e =  xmlGetProp(ptr, (xmlChar*)"stop");
				c =  xmlGetProp(ptr, (xmlChar*)"channel");
				if (s && e && c)
				{
					XMLTV_STRINGS_STRUCT * str = NULL;
					printf("[RESTREAM] Found XML entry [start: %s] [end: %s] [xml id: %s]\n", s, e, c);
					title = xmltv_get_program_title(x, (char *)c, (char *)s);
					str = xmltv_get_channel_display(x, (char *)c);
					if (str)
					{
						int i;
//						printf("HI\n");
//						for(i=0; i < str->count; i++)
//							printf("HI: %s\n", str->strs[i]);
						xmlChar *id = str->strs[0];
						if (str->count == 5)
						{
//							if (strstr((char *)str->strs[3], "(") ==  NULL)
//								id = str->strs[3];
							if (strstr((char *)str->strs[restream_dev->xmltv_display_index], "(") ==  NULL)
								id = str->strs[restream_dev->xmltv_display_index];
						}
						else
							printf("[RESTREAM] _restream_fill_timer_xmltv, count bad: %d (%s)\n", str->count, (char*)id);

						chname = (xmlChar*) strdup((char*)id);
						for(i=0; i < str->count; i++)
							xmlFree(str->strs[i]);
//						printf("HI2: %s\n", chname);

					}
					else
						printf("SAD\n");
//					chname = xmltv_get_channel_display(x, c);
					stitle = xmltv_get_program_subtitle(x, (char *)c, (char *)s);
					cat = xmltv_get_program_category(x, (char *)c, (char *)s);
					desc = xmltv_get_program_desc(x, (char *)c, (char *)s);

					if (!timer->data.channel_num)
						timer->data.channel_num = _restream_extract_channelnum((char *)c);

					if (!timer->data.start_epoch)
						timer->data.start_epoch = utils_str_to_epoch((char *)s);

					if (!timer->data.end_epoch)
						timer->data.end_epoch = utils_str_to_epoch((char *)e);

//					printf("HMM: %" PRId64"\n", timer->data.channel_num);

					if (chname)
					{
						strncpy(timer->data.channel_name, (char *)chname, sizeof(timer->data.channel_name));
//						printf("chname: %s\n", timer->data.channel_name);
					}
					if (title)
					{
						strncpy(timer->data.title, (char *)title, sizeof(timer->data.title));
//						printf("title: %s\n", timer->data.title);
					}

					if (stitle)
					{
						strncpy(timer->data.plot_outline, (char *)stitle, sizeof(timer->data.plot_outline));
//						printf("plot_outline: %s\n", timer->data.plot_outline);
					}

					if (cat)
					{
						strncpy(timer->data.genre, (char *)cat, sizeof(timer->data.genre));
//						printf("genre: %s\n", timer->data.genre);
					}

					if (desc)
					{
						strncpy(timer->data.plot, (char *)desc, sizeof(timer->data.plot));
//						printf("plot: %s\n", timer->data.plot);
					}

				}

				if (s) xmlFree(s);
				if (e) xmlFree(e);
				if (c) xmlFree(c);
				if (title) free(title);
				if (chname) free(chname);
				if (stitle) free(stitle);
				if (cat) free(cat);
				if (desc) free(desc);

//				xmlFreeNode(ptr);
			}
			else
			{
				utils_log_print(restream_dev->device->log,  "MO iz sad\n");
			}

		}
		else
		{
			printf("_restream_fill_timer_xmltv, x: %p, dd: %p\n", x, dd_progid);
		}
		if (dd_progid)
			free(dd_progid);

//		if (x)
//			xmltv_cleanup(x);
		return 0;
	}
	return 1;
}

int _restream_get_dd_progid(TUNER_RESTREAM_STRUCT * restream_dev, TIMER_STRUCT *timer_data)
{
	xmlNodePtr tmpptr = NULL;
//	char *start=NULL, *end=NULL, *chan_name=NULL;
	xmlChar *start=NULL, *end=NULL;
	//XMLTV_STRUCT *x = xmltv_init(restream_dev->xmltv_file);
	XMLTV_STRUCT * x = _restream_get_xmltv(restream_dev);

	if (x)
	{
		start = (xmlChar *)utils_epoch_to_str(timer_data->data.start_epoch);
		end = (xmlChar *)utils_epoch_to_str(timer_data->data.end_epoch);
//		chan_name = xmltv_get_channel_display(x, timer_data->data.url);

		tmpptr = xmltv_get_id_and_times(x, timer_data->data.url, (char*)start, (char*)end);

		if (tmpptr)
		{
			char *dd = (char*)xmltv_get_episode_dd(x, timer_data->data.url, (char*)start, (char*)end);
			if (dd)
			{
				//printf("[%s] using dd_progid: %s\n", chan_name, dd);
				timer_data->data.id = TIMER_USE_DD_PROGID;
				strncpy(timer_data->data.title, dd, TIEMR_STRUCT_MAX_STRLEN -1);
				xmlFree(dd);

			}
//			xmlFreeNode(tmpptr);
		}

//		xmltv_cleanup(x);
//		free(x);
	}

	if (start)
		xmlFree(start);

	if (end)
		xmlFree(end);
/*
	if (chan_name)
	{
		strncpy(timer_data->data.channel_name, chan_name, TIEMR_STRUCT_MAX_STRLEN -1);
		xmlFree(chan_name);
	}
*/
	return 0;
}

int _restream_dev_wait(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->init_done)
			return 0;
	}
	return 1;
}
int _restream_reload_epg(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->dataptr && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		XMLTV_STRUCT * tmp = restream_dev->xmltv_cache;
//		DEVICE_STRUCT *device = tpass->device;
		printf("DO EPG RELOAD ERE1\n");
		
		restream_dev->xmltv_cache = NULL;
		if (_restream_get_channel_names_from_xmltv(restream_dev) == 0)
		{
			printf("[RESTREAM] Freeing old xml data\n");
			xmltv_cleanup(tmp);
			printf("[RESTREAM] Freeing done!\n");
		}
		else
		{
			printf("[RESTREAM] Problem reading XML, using old data\n");
			restream_dev->xmltv_cache = tmp;
		}

		printf("DO EPG RELOAD ERE2\n");
		return 0;
	}
	return 1;
}
int _restream_get_chan_epg(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->dataptr && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		DEVICE_STRUCT *device = tpass->device;
		TUNER_CHANNEL_STRUCT * chan = NULL;

		TIMER_STRUCT_SEND *tsend = (TIMER_STRUCT_SEND *)tpass->dataptr;
		uint32_t c1 = atoi(tsend->url);

		chan = device_channel_get_struct(device, c1, 0);

		if (chan)
		{
			int rc;
			uint64_t count = 0;
	//		printf("_restream_get_chan_epg: %d %s\n", c1, chan->xmltv_id);

			TIMER_STRUCT_EXTRA *extra = NULL, extra_dummy = {0};;
			TIMER_STRUCT * timer = NULL, *del = NULL;


			if (restream_dev->zap2it_lineup && restream_dev->zap2it_zipcode)
			{
				timer = zap2it_get_epg(restream_dev->zap2it_epg, c1, tsend->start_epoch, tsend->end_epoch);
			}
			else
			{
				XMLTV_STRUCT *x = _restream_get_xmltv(restream_dev);
				timer = xmltv_get_epg_list(x, chan->xmltv_id, tsend->start_epoch, tsend->end_epoch);
			}

			if (timer)
			{
				extra = (TIMER_STRUCT_EXTRA *)timer->extra_data;
				//printf("get_epg, chane: id: %s, count: %" PRId64" (%s) [s: %" PRId64"] [e: %"PRId64"]\n", tsend->url, extra->epg_count, tpass->ip_addr, tsend->start_epoch, tsend->end_epoch);
				count = extra->epg_count;
			}

			if (count == 0 || !timer)
			{
				if (chan->xmltv_id && strstr(chan->xmltv_id,RESTREAM_EPG_PROTO_STR) != NULL)
				{
					char *forward = chan->xmltv_id + strlen(RESTREAM_EPG_PROTO_STR);
					if (forward)
					{
						UTILS_HOSTPORT_STRUCT *hostport = utils_split_host(forward);
						if (hostport)
						{
							TIMER_STRUCT_SEND timer_send = {0};
							int sock, sz, err;
							strncpy(timer_send.url, tsend->url, sizeof(timer_send.url) - 1);

							printf("[RESTREAM] forward epg request to: %s, port: %d, xml: %s, id: %s\n", hostport->hostname, hostport->port, chan->xmltv_id, tsend->url);
							//protocol_get_channel_epg(hostport->hostname, hostport->port, &sock, tsend->url);
							protocol_get_channel_epg(hostport->hostname, hostport->port, &sock, &timer_send);
							if (sock)
							{
								TIMER_STRUCT tmp_timer = {{0}}, *_timer = NULL;
								TIMER_STRUCT_EXTRA tmp_extra = {0}, *_extra = NULL;

//								uint64_t count = 0;
								sz = get_bytes_from_socket(sock, (uint8_t*)&count, sizeof(count), 0, &err);
								printf("[RESTREAM] forward epg count: %" PRId64"\n", count);

								if (sz == sizeof(count))
								{
									while(count && tmp_timer.data.is_last == 0)
									{
										sz = get_bytes_from_socket(sock, (uint8_t*)&tmp_timer.data, sizeof(TIMER_STRUCT_SEND), 0, &err);
										if (sz == sizeof(TIMER_STRUCT_SEND))
										{
											_timer = calloc(1, sizeof(*_timer));
											sz = get_bytes_from_socket(sock, (uint8_t*)&tmp_extra, sizeof(TIMER_STRUCT_EXTRA), 0, &err);

											if (sz == sizeof(TIMER_STRUCT_EXTRA))
											{
												_extra = calloc(1, sizeof(*_extra));
												if (_extra)
													memcpy(_extra, &tmp_extra, sizeof(TIMER_STRUCT_EXTRA));
											}

											if (_timer)
											{
												memcpy(&_timer->data, &tmp_timer.data, sizeof(TIMER_STRUCT_SEND));
												_timer->extra_data = (void*)_extra;

												if (!timer)
												{
													timer = _timer;
												}
												else
												{
													TIMER_STRUCT *_t2 = timer;
													while(_t2->next)
														_t2 = _t2->next;
													_t2->next = _timer;
												}
											}
										}
									}
								}
								close(sock);
							}
							free(hostport);
						}
//						printf("[RESTREAM] forward epg request to: %s\n", forward);
					}
				}
				else
				{
					printf("BLEAH, c: %"PRId64" p: %p, chan_id: %s, xmltv_id: %s\n", count, timer, tsend->url, chan->xmltv_id);
				}

				if (!timer)
					return -1;
			}

			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&count, sizeof(count), 5000, NULL);

			if (rc != sizeof(count))
			{
				printf("_restream_get_chan_epg, err 1: %d\n", rc);
				return -1;
			}

			while (timer)
			{
				if (timer->next == NULL)
					timer->data.is_last = 1;

				extra = (TIMER_STRUCT_EXTRA *)timer->extra_data;
				if (!extra)
					extra = &extra_dummy;
//				printf("Title: %s (s: %" PRId64 ", e: %" PRId64") Name: %s\n", timer->data.title, timer->data.start_epoch, timer->data.end_epoch, timer->data.channel_name);

				rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&timer->data, sizeof(TIMER_STRUCT_SEND), 5000, NULL);
				if (rc != sizeof(TIMER_STRUCT_SEND))
				{
					printf("_restream_get_chan_epg, err 2: %d\n", rc);
					return -1;
				}

				rc = write_bytes_to_socket(tpass->sock, (uint8_t *)extra, sizeof(TIMER_STRUCT_EXTRA), 5000, NULL);
				if (rc !=  sizeof(TIMER_STRUCT_EXTRA))
				{
					printf("_restream_get_chan_epg, err 3: %d\n", rc);
					return -1;
				}

				del = timer;
				timer = timer->next;

				if (del)
				{
					if (del->extra_data)
						free(del->extra_data);
					free(del);
				}
			}
		}
		else
		{
			printf("BHA\n");
	printf("_restream_get_chan_epg 5: %d %s\n", c1, tsend->url);
		}
	}
	return 0;
}

int _restream_timer_add(TUNER_INITPASS_STRUCT *tpass)
{
	int ret = -1;
	if (tpass && tpass->dataptr && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;

		if (restream_dev->timer_ctl)
		{
			TIMER_STRUCT __timer_data = {{0}};
			TIMER_STRUCT *timer_data = &__timer_data;

			if (tpass->dataptr)
			{
//				int q;
				TIMER_STRUCT timer_data_org = {{0}};
				RESTREAM_CHANNEL_URL_LIST * tmp_list = _restream_build_tuner_list(restream_dev);

				memcpy(&timer_data->data, tpass->dataptr, tpass->datasz);

				timer_data_org.data.start_epoch = timer_data->data.start_epoch;
				timer_data_org.data.end_epoch = timer_data->data.end_epoch;
				timer_data_org.data.channel_num = timer_data->data.channel_num;

//				timer_print_timer(timer_data);

				if (timer_data->data.channel_num)
				{
					if (timer_data->data.url[0] == 0)
					{
						TUNER_CHANNEL_STRUCT *  chan =  device_channel_get_struct(tpass->device, timer_data->data.channel_num, 0);
						if (chan)
						{
							snprintf(timer_data->filename, TIEMR_STRUCT_MAX_STRLEN-1, "%" PRId64"_%" PRId64"_%" PRId64"", timer_data->data.start_epoch, timer_data->data.end_epoch, timer_data->data.channel_num);
//							snprintf(timer_data->recording_dir, TIEMR_STRUCT_MAX_STRLEN-1, "%s", restream_dev->timer_ctl->record_dir);
							snprintf(timer_data->data.url, TIEMR_STRUCT_MAX_STRLEN-1, "%s", chan->xmltv_id);
							printf("[RESTREAM] Add Timer, Filename: %s, %s\n", timer_data->filename, timer_data->data.url);
//							printf("[RESTREAM] Add Timer, Rec Dir: %s, Filename: %s, %s\n", timer_data->recording_dir, timer_data->filename, timer_data->data.url);
						}
						else
						{
							printf("[RESTREAM] [TimerAdd] BAD 1!\n");
							return -1;
						}
						memcpy(&timer_data_org, timer_data, sizeof(TIMER_STRUCT));

//						printf("[RESTREAM] [TimerAdd] Setting  URL to: %s\n", timer_data->data.url);
						if (restream_dev->zap2it_lineup && restream_dev->zap2it_zipcode)
						{
							timer_data->data.id = 0;
						}
						else
						{
							timer_data->data.channel_num = 0;
						}
					}
				}
				else if (timer_data->data.channel_num == 0 && timer_data->data.id != TIMER_USE_DD_PROGID)
				{
					printf("[RESTREAM] [TimerAdd] BAD 2!\n");
					return -1;
				}


				if (!timer_data->data.channel_num && timer_data->data.start_epoch && timer_data->data.end_epoch)
				{
					printf("[RESTREAM] searching for dd_progid, %" PRId64" -- %" PRId64" -- %s\n", timer_data->data.start_epoch, timer_data->data.end_epoch, timer_data->data.url);
					//timer_data->data.channel_num = _restream_extract_channelnum(timer_data->data.url);
					_restream_get_dd_progid(restream_dev, timer_data);
				}
				else
				{
					printf("[RESTREAM] NO dd_progid, %" PRId64" -- %" PRId64" -- %s\n", timer_data->data.start_epoch, timer_data->data.end_epoch, timer_data->data.url);
				}

				if (timer_data->data.id == TIMER_USE_DD_PROGID)
				{
					printf("using dd_id\n");
					_restream_fill_timer_xmltv(restream_dev, timer_data);
				}
				else
				{
					timer_data->data.start_epoch = timer_data_org.data.start_epoch;
					timer_data->data.end_epoch = timer_data_org.data.end_epoch;
					timer_data->data.channel_num = timer_data_org.data.channel_num;

//					memcpy(timer_data, &timer_data_org, sizeof(TIMER_STRUCT));
					printf("NOT using dd_id, chan_num: %" PRId64", name: %s -- s: %"PRId64", e: %"PRId64"\n", timer_data->data.channel_num, timer_data->data.channel_name, timer_data->data.start_epoch, timer_data->data.end_epoch);
				}

				if (tmp_list)
				{
					if (restream_dev->zap2it_lineup && restream_dev->zap2it_zipcode)
						timer_data = zap2it_get_epg(restream_dev->zap2it_epg,  timer_data_org.data.channel_num, timer_data_org.data.start_epoch, timer_data_org.data.end_epoch);

					if (_restream_find_tuner(restream_dev, timer_data, tmp_list))
					{
		//				printf("Adding: %" PRId64" -- %s\n", timer_data->data.channel_num, timer_data->data.url);

						if (!timer_data->data.start_epoch)
							timer_data->data.start_epoch = time(NULL);


						timer_data->data.start_epoch -= restream_dev->timer_ctl->pre_pad;
						timer_data->data.end_epoch += restream_dev->timer_ctl->post_pad;

						utils_log_print(restream_dev->device->log,  "[RESTREAM] Adding Timer: %" PRId64" (url: %s) -- s: %" PRId64", e: %" PRId64"\n", timer_data->data.channel_num, timer_data->data.url, timer_data->data.start_epoch, timer_data->data.end_epoch);
						timer_add_timer(restream_dev->timer_ctl, timer_data);
						timers_save_file(restream_dev->timer_ctl);


						ret = 0;
					}
					else
					{
						utils_log_print(restream_dev->device->log,  "[RESTREAM] Cannot add timer: %" PRId64"\n", timer_data->data.channel_num);
						ret = 1;
					}
					if (restream_dev->zap2it_lineup && restream_dev->zap2it_zipcode)
						free(timer_data);

				}
			}

		}
	}
	return ret;
}

int _restream_timer_del(TUNER_INITPASS_STRUCT *tpass)
{
//			printf("_restream_timer_del start\n");
	if (tpass && tpass->device && tpass->device->parent)
	{
		int rc = 0;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->timer_ctl)
		{
			rc = timer_del_timer(restream_dev->timer_ctl, tpass->id);
//			printf("_restream_timer_del 3, rc: %d\n", rc);
		}
		return rc;
	}
	return -1;
}

/*
int _restream_timer_stop(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->timer_ctl)
		{
//			return timer_del_timer(restream_dev->timer_ctl, tpass->id);
		}

	}
	return -1;
}
*/
int _restream_timer_list(TUNER_INITPASS_STRUCT *tpass)
{
//	printf("M1\n");
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->timer_ctl)
		{
			TIMER_STRUCT * first = timer_list_timer(restream_dev->timer_ctl);
			utils_log_print(restream_dev->device->log,  "timer lcount: %d (%s)\n", restream_dev->timer_ctl->count, tpass->ip_addr);
//			printf("timer_list count: %d\n", restream_dev->timer_ctl->count);
			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&restream_dev->timer_ctl->count, sizeof(restream_dev->timer_ctl->count), 5000, NULL);
			if (rc != sizeof(restream_dev->timer_ctl->count))
			{
				printf("_restream_get_chan_epg, err 4\n");
				return -1;
			}

			while(first)
			{
				if (first->next == NULL)
					first->data.is_last = 1;

//				printf("HMM, id: %d (%s)\n", first->id, first->channel_name);
				rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&first->data, sizeof(TIMER_STRUCT_SEND), 5000, NULL);
				if (rc != sizeof(TIMER_STRUCT_SEND))
				{
					printf("_restream_get_chan_epg, err 5\n");
					return -1;
				}

				first = first->next;
			}

			return 0;
		}
	}
	return -1;
}

int _restream_timer_seqnum(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->timer_ctl)
		{
//			utils_log_print(restream_dev->device->log,  "timer_list seq# %" PRId64"\n", restream_dev->timer_ctl->sequence_num);
			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&restream_dev->timer_ctl->sequence_num, sizeof(restream_dev->timer_ctl->sequence_num), 5000, NULL);
			if (rc != sizeof(restream_dev->timer_ctl->sequence_num))
			{
				printf("_restream_get_chan_epg, err 6\n");
				return -1;
			}

			return 0;
		}
	}
	return -1;
}

int _restream_timer_count(TUNER_INITPASS_STRUCT *tpass)
{
//	printf("M1\n");
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		if (restream_dev->timer_ctl)
		{
//			TIMER_STRUCT * first = timer_list_timer(restream_dev->timer_ctl);
//			utils_log_print(restream_dev->device->log,  "timer_list count: %d\n", restream_dev->timer_ctl->count);
			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&restream_dev->timer_ctl->count, sizeof(restream_dev->timer_ctl->count), 5000, NULL);
			if (rc != sizeof(restream_dev->timer_ctl->count))
			{
				printf("_restream_get_chan_epg, err 7\n");
				return -1;
			}

			return 0;
		}
	}
	return -1;
}

int _restream_rec_stop(TUNER_INITPASS_STRUCT *tpass)
{
//	int rc;
//	if (tpass && tpass->dataptr && tpass->sock > 2 && tpass->device && tpass->device->parent)
	if (tpass && tpass->extra_data[0])
	{
		TIMER_STRUCT *timer = (TIMER_STRUCT *)tpass->extra_data[0];
		TIMER_CONTROL_STRUCT *control = timer->parent;
//	printf("_restream_rec_stop: %d\n", timer->data.id);
		timer->data.end_epoch = time(NULL) - control->post_pad - 2;
		while(timer->data.status != TIMER_STATUS_INVALID)
		{
			printf("Waiting for timer to die... 0x%x\n", timer->data.status);
			usleep(1000*2000 * 2);
		}
	printf("_restream_rec_stop: %d\n", timer->data.id);
	}
	return 0;
}
int _restream_rec_play(TUNER_INITPASS_STRUCT *tpass)
{
//	printf("_restream_rec_play\n");
	if (tpass && tpass->dataptr && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		TIMER_CONTROL_STRUCT * timer_ctl = restream_dev->timer_ctl;
		MPEGTS_IO_RECORDINGS_STRUCT * recordings = restream_dev->recordings;
		uint32_t id = 0;

		if (tpass->dataptr)
		{
			memcpy(&id, tpass->dataptr, tpass->datasz);
			free(tpass->dataptr);
		}
		tpass->dataptr = NULL;
		tpass->datasz = 0;
//		printf("_restream_rec_play id: %d\n", id);
		_restream_reload_recordings(restream_dev);
		recordings = restream_dev->recordings;

		if (recordings)
		{
			TIMER_STRUCT *timer = NULL;
			MPEGTS_IO_STRUCT * first = recordings->first_io_struct;
//			printf("_restream_rec_play 2\n");

			while(first)
			{
				first->isrecording = 1;
				first->islive = 0;
/*
				printf("\t%d (%s)\n", first->id, first->base_filename);
				if (first->first_fileunit)
				{
					printf("_restream_rec_play 3, haz first_fileunit\n");
					if (first->first_fileunit->first_idx)
						printf("_restream_rec_play 3, haz first_idx\n");
					else
						printf("_restream_rec_play 3, no first_idx :(\n");
				}
				else
					printf("_restream_rec_play 3, no first_fileunit :(\n");

*/
				if (first->extra_data[1])
				{

					timer = (TIMER_STRUCT *)first->extra_data[1]->data;
//		printf("_restream_rec_play 4 -- %d %d\n", timer->data.id, id);

					if (timer && timer->data.id == id)
					{
						if (timer_ctl && timer_ctl->count)
						{
							int i = 0;
							for(i=0; i < TIMER_MAX_TIMERS; i++)
							{
								if (timer_ctl->timers[i] && timer_ctl->timers[i]->data.channel_num && timer_ctl->timers[i]->data.channel_num  == timer->data.channel_num)
									break;
							}

							if (i < timer_ctl->count)
							{
								utils_log_print(restream_dev->device->log, "-- FOUND ID [i: %d], state: %d [%" PRId64" -- %s]\n", i, timer->data.status, timer->data.channel_num, timer->data.channel_name);
								tpass->id = id;
								tpass->channel_num = timer_ctl->timers[i]->data.channel_num;
								tpass->r_state = timer_ctl->timers[i]->data.status;
								tpass->extra_data[0] = (void*) &timer_ctl->timers[i];
							}
						}

						tpass->dataptr = (uint8_t*) first;
						tpass->datasz = sizeof(MPEGTS_IO_STRUCT);
						return 0;

					}
				}
				else
				{
					printf("B1\n");
				}
//				printf("L3\n");

				if (!timer)
					utils_log_print(restream_dev->device->log,  "sad timer\n");

				first = first->next;
			}
//	printf("_restream_rec_play loop end\n");

		}
		else
		{
			printf("_restream_rec_play none\n");
		}
	}
	return -1;
}

int _restream_rec_del(TUNER_INITPASS_STRUCT *tpass)
{
	int rc;
	if (tpass && tpass->dataptr && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
		MPEGTS_IO_RECORDINGS_STRUCT * recordings = restream_dev->recordings;
		uint32_t id = 0;
		memcpy(&id, tpass->dataptr, tpass->datasz);
		utils_log_print(restream_dev->device->log, "del recording: %d\n", id);

		if (recordings)
		{
			uint8_t ret = PROTOCOL_INIT_BAD;
			int i, err;
			TIMER_STRUCT *timer = NULL;
			MPEGTS_IO_STRUCT * first = restream_dev->recordings->first_io_struct;
//			utils_log_print(restream_dev->device->log, "recording count: %d\n", recordings->count);

			for(i=0; i < recordings->count; i++)
			{
				timer = (TIMER_STRUCT *)first->extra_data[1]->data;
//				printf("HERE1 -- %s -- %d\n", timer->data.channel_name, timer->data.id);

				if (timer && timer->data.id == id)
				{
					char fname[4096] = {0};
					printf("HERE2 DEL ME: %s (state: %d)\n", first->base_filename, timer->data.status);

					snprintf(fname, 4095, "%s.rec", first->base_filename);
					remove(fname);
					snprintf(fname, 4095, "%s.idx", first->base_filename);
					remove(fname);
					snprintf(fname, 4095, "%s.mpg", first->base_filename);
					remove(fname);

					ret = PROTOCOL_INIT_GOOD;
					printf("reloading...\n");
					_restream_reload_recordings(restream_dev);
					printf("reload done...\n");
					break;

//					rc = write_bytes_to_socket(tpass->sock, (uint8_t *)timer, sizeof(TIMER_STRUCT), 5000, NULL);
				}
				first = first->next;
			}
			printf("Sending back: %d\n", ret);
			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&ret, sizeof(ret), 5000, &err);
			if (rc != sizeof(ret))
			{
				printf("_restream_get_chan_epg, err 8\n");
				return -1;
			}

			return 0;
		}

	}
	return 0;
}

int _restream_rec_seqnum(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;

		_restream_reload_recordings(restream_dev);

		MPEGTS_IO_RECORDINGS_STRUCT * recordings = restream_dev->recordings;

		if (recordings)
		{
//			utils_log_print(restream_dev->device->log,  "recording seq# %" PRId64"\n", recordings->sequence_num);

			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&recordings->sequence_num, sizeof(recordings->sequence_num), 5000, NULL);
			if (rc != sizeof(recordings->sequence_num))
			{
				printf("_restream_get_chan_epg, err 9\n");
				return -1;
			}

			return 0;
		}
	}
	return -1;
}

int _restream_rec_count(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;

		_restream_reload_recordings(restream_dev);

		MPEGTS_IO_RECORDINGS_STRUCT * recordings = restream_dev->recordings;

		if (recordings)
		{
//			MPEGTS_IO_STRUCT * first = restream_dev->recordings->first_io_struct;
//			utils_log_print(restream_dev->device->log, "recording count: %d\n", recordings->count);

			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&recordings->count, sizeof(recordings->count), 5000, NULL);
			if (rc != sizeof(recordings->count))
			{
				printf("_restream_get_chan_epg, err 10\n");
				return -1;
			}

			return 0;
		}
	}
	return -1;
}

int _restream_rec_list(TUNER_INITPASS_STRUCT *tpass)
{
	if (tpass && tpass->sock > 2 && tpass->device && tpass->device->parent)
	{
		int rc;
//		printf("_restream_rec_list 1\n");
		TUNER_RESTREAM_STRUCT * restream_dev = (TUNER_RESTREAM_STRUCT *)tpass->device->parent;
//		printf("MOOOO?\n");

		_restream_reload_recordings(restream_dev);

//		printf("_restream_rec_list 2\n");
		MPEGTS_IO_RECORDINGS_STRUCT * recordings = restream_dev->recordings;

		if (recordings)
		{
			TIMER_STRUCT *timer = NULL;
			MPEGTS_IO_STRUCT * first = restream_dev->recordings->first_io_struct;
//			utils_log_print(restream_dev->device->log, "recording count: %d\n", recordings->count);
//		printf("_restream_rec_list 3\n");

			rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&recordings->count, sizeof(recordings->count), 5000, NULL);
			if (rc != sizeof(recordings->count))
			{
				printf("_restream_rec_list, err 1\n");
				return -1;
			}
//			printf("_restream_rec_list rc1: %d\n", rc);

			while(first)
			{
				timer = (TIMER_STRUCT *)first->extra_data[1]->data;
//				printf("HERE1 -- %s\n", timer->data.channel_name);

				if (timer)
				{
//					printf("HERE2\n");
					if (first->next == NULL)
						timer->data.is_last = 1;

					rc = write_bytes_to_socket(tpass->sock, (uint8_t *)&timer->data, sizeof(TIMER_STRUCT_SEND), 5000, NULL);
					if (rc != sizeof(TIMER_STRUCT_SEND))
					{
						printf("_restream_rec_list, err 2\n");
						return -1;
					}

				}
				first = first->next;
			}
//		printf("_restream_rec_list 4\n");

//	printf("R4\n");

			return 0;
		}
	}
//		printf("_restream_rec_list 5\n");

//	printf("Rz\n");
	return -1;
}

//int _restream_http_connect(TUNER_STRUCT *tuner, TUNER_CHANNEL_STRUCT *chan_struct)
int _restream_http_connect(TUNER_CHANNEL_STRUCT *chan_struct, TUNER_RESTREAM_STRUCT *dev)
{
	if (chan_struct && chan_struct->url_count)
	{
		int sock = 0, sz, i, j, retry = 0, err = 0;
		char *host = NULL, *tmpp = NULL;
		char req[4096] = {0}, in=0, last=0;
		PROTOCOL_TUNE_STRUCT *_ptmp = (PROTOCOL_TUNE_STRUCT *)chan_struct->prot_params;
		char *append = chan_struct->url_params;

		for(i=0; i < chan_struct->url_count; i++)
		{
			last = i;
			sz = strlen(chan_struct->urls[i]);
			host = strdup(chan_struct->urls[i]);

			if (host)
			{
				printf("[RESTREAM] Trying to connect to: %s\n", chan_struct->urls[i]);
				for (j=0; j < sz; j++)
				{
					if (host[j] == ':')
					{
						host[j] = '\0';
						tmpp = &host[j+1];
					}
					else if (host[j] == '/')
					{
						host[j] = '\0';
					}
				}
				sock = doconnect(host, atoi(tmpp), 0);

				free(host);
				host = NULL;

				if (sock <= 0)
				{
					printf("[RESTREAM] Problem connecting: %s -- %s\n", strerror(errno), chan_struct->urls[i]);
					continue;
				}

				if (_ptmp)
				{
/*
					printf("[RESTREAM] Tune Params:\n");
					printf("[RESTREAM]\tstarting_bandwidth: %" PRId64"\n",  _ptmp->starting_bandwidth);
					printf("[RESTREAM]\tgame_datetime: %" PRId64"\n",  _ptmp->game_datetime);
//					printf("[RESTREAM]\tcdn: %d\n",  _ptmp->cdn);
*/
					sprintf(req, "GET /EXT/%d&sbitrate=%" PRId64"&minbitrate=%" PRId64"&maxbitrate=%" PRId64"&gamedatetime=%" PRId64"&gamestarttime=%" PRId64"&adaptive=%d&cdn=%d", chan_struct->virtual_channel, _ptmp->starting_bandwidth, _ptmp->min_bandwidth, _ptmp->max_bandwidth,  _ptmp->game_datetime, _ptmp->game_starttime, _ptmp->adaptive, _ptmp->cdn);
					if (strlen(_ptmp->aproxy))
					{
						strcat(req, "&aproxy=");
						strcat(req, _ptmp->aproxy);
					}
					if (strlen(_ptmp->fproxy))
					{
						strcat(req, "&fproxy=");
						strcat(req, _ptmp->fproxy);
					}
					strcat(req, " HTTP/1.0\n\n");
				}
				else if (append)
				{
					sprintf(req, "GET /EXT/%d&%s HTTP/1.0\n\n", chan_struct->virtual_channel, append);
//					printf("....... ptmp is sad\n");
				}
				else
				{
					sprintf(req, "GET /EXT/%d HTTP/1.0\n\n", chan_struct->virtual_channel);
				}

//				printf("[RESTREAM] remote req url: %s", req);
				sz = write_bytes_to_socket(sock, (uint8_t*)req, strlen(req), 5000, NULL);

				if (sz <= 0)
				{
//					printf("[RESTREAM] Error sending GET req\n");
					close(sock);
					continue;
				}
//				printf("Wrote: %d\n", sz);
//			printf("5\n");

				memset(&req, 0, sizeof(req));

				sz = 0;
//				printf("1\n");
				while(sz == 0)
				{
					sz = get_bytes_from_socket(sock, (uint8_t*)&req, 17, 100, &err);
					if (sz < 0)
					{
						printf("get_bytes_from_socket FAILED!!\n");
						close(sock);
						continue;
					}
					else if (sz == 0)
					{
//						printf("get_bytes_from_socket not good, trying again (%s) (%d)\n", strerror(errno), sz);
					}
				}
//				printf("2\n");

				if (memcmp(&req, RESTREAM_HTTP_OK_STR, strlen(RESTREAM_HTTP_OK_STR)) == 0)
				{
					int count=0;
					j = 2;

					do
					{
						last = in;
						sz = get_bytes_from_socket(sock, (uint8_t*)&in, 1, 100, &err);
						count++;

						if (last == 0xd && in == 0xa)
							j--;

						if (count > TS_PACKET_SIZE)
							break;
					}
					while (j);

					if (!j)
					{
//						utils_log_print(dev->device->log, "[RESTREAM] Connection to %s is good (ch: %d)\n", chan_struct->urls[i], chan_struct->virtual_channel);
						return sock;
					}
				}
				else
				{
					utils_log_print(dev->device->log, "[RESTREAM] Connection not good, sad:( -- %s\n", strerror(errno));
					retry++;
					i--;
					if (retry > 3)
						i = chan_struct->url_count;
					else
						utils_log_print(dev->device->log, "[RESTREAM] retrying\n");
//					continue;
				}
//			printf("7\n");
			}
			else
			{
				 utils_log_print(dev->device->log, "[RESTREAM] Connection to %s%d failed, trying next in list...\n", chan_struct->urls[i], chan_struct->virtual_channel);
			}
			close(sock);
		}
//		printf("Loop end\n");

	}
//	printf("_restream_http_connect end\n");

	return 0;
}

int _restream_get_channel_names_from_xmltv(TUNER_RESTREAM_STRUCT *dev)
{
//restream_dev->xmltv_cache->cached = 1;
	if (dev && !dev->xmltv_cache && dev->xmltv_file)
	{
		_restream_get_xmltv(dev);
	}

	if (dev && dev->device && dev->xmltv_cache && dev->xmltv_cache->cached == 0)
	{
		XMLTV_STRUCT * x = _restream_get_xmltv(dev);
		char tmp[255] = {0};
		TUNER_CHANNEL_STRUCT * chan = dev->device->first_channel_list;

		if (!x)
			printf("WTF?!?!?!\n");
		printf("[RESTREAM] [xMLTV] - Caching channel names .... \n");
		while(x && chan)
		{
			sprintf(tmp, "%s", chan->xmltv_id);
//			printf("HI1: %s -- %s\n", tmp, chan->xmltv_id);
//			sprintf(tmp, "I%d.labs.zap2it.com", (uint32_t)chan->virtual_channel);
			XMLTV_STRINGS_STRUCT * str = xmltv_get_channel_display(x, tmp);
			if (str)
			{
//			printf("HI2: %d\n", str->count);
				if (str->count > 0)
				{
					char *id = NULL;
					int i;
//					for(i=0; i < str->count; i++)
//						printf("BL:EAH: %s\n", str->strs[i]);

					id = (char*)str->strs[0];
					if (str->count == 5)
					{
//						if (strstr((char*)str->strs[3], "(") ==  NULL)
//							id = (char*)str->strs[3];

//						if (strstr((char*)str->strs[dev->xmltv_display_index], "(") ==  NULL)
//							id = (char*)str->strs[dev->xmltv_display_index];
						id = (char*)str->strs[str->count - 2];
					}
					else
						printf("[RESTREAM] _restream_get_channel_names_from_xmltv, count bad: %d (%s) (%s)\n", str->count, (char*)id, (char*)str->strs[str->count - 2]);

					if (id)
					{
						char *fix = strstr(id, " (");

						if (fix)
						{
							char *fix2 = strstr(id, "(Pac");
							if (fix2)
							{
								fix2[2] = ')';
								fix2[3] = '\0';
							}
							else
							{
								fix[0] = '\0';
							}
						}
//						printf("\t%s\n", id);
//							id = (char*)str->strs[dev->xmltv_display_index];
//						device_channel_add_name(dev->device, chan->virtual_channel, chan->program_num, id);
						strncpy(chan->name, id, MAX_DEVNAME_SIZE);
					}

					for(i=0; i < str->count; i++)
						free(str->strs[i]);
				}
				free(str);
			}

//			snprintf(chan->info, MAX_DEVNAME_SIZE, "http://fileserver2.lan/xmltv/icons/%s.png", tmp);
//			_dev_internal_printf(dev->device, "[DEV-CHAN] Virtual Chan: %d, PGM: %" PRId64" (name: %s)\n", chan->virtual_channel, chan->program_num, chan->name);
			chan = chan->next;
		}
		printf("[RESTREAM] [xMLTV] - Caching done\n");

		dev->xmltv_cache->cached = 1;
	}
	return 0;
}


int _restream_get_channel_lineup(TUNER_RESTREAM_STRUCT *dev)
{
	int filehandle = -1;
	char *tmp_mem = NULL;
	off_t sz = 0;

	if (!dev)
	{
//	 	printf("[RESTREAM] _restream_get_channel_lineup: device is null\n");
		return 1;
	}

	filehandle = open(dev->channels_file, O_RDONLY);

	if (filehandle == -1)
	{
	 	utils_log_print(dev->device->log, "[RESTREAM] Can't open tuner config\n");
		return 1;
	}

	sz = get_file_size(dev->channels_file);
	tmp_mem = calloc(1, sz);

	if (tmp_mem)
	{
		int bytes_read = 0;
		TEXTFILE_IN_MEMORY *memf = NULL;
		char * line = NULL;

		lseek(filehandle, 0, SEEK_SET);
		bytes_read = get_bytes_from_file(filehandle, (uint8_t*)tmp_mem, sz);

		close(filehandle);
		if (bytes_read != sz)
		{
			utils_log_print(dev->device->log, "READ ERRORORORORO: %d\n", bytes_read);
			return 0;
		}

		memf = memfile_init((char*)tmp_mem, (int)bytes_read);
		if (memf)
		{
			int p;
			int line_count = memfile_getline_count(memf);
//			char *chanmap[6] = {0};
			utils_log_print(dev->device->log, "[RESTREAM] Parsing Channel-Map from: %s.. \n", dev->channels_file);

			for (p=0; p < line_count; p++)
			{
				line = memfile_getnext_line(memf, 1);

				if (line)
				{
					int sock = 0, sz = 0, i;
					int count = 0;
					char *_tmpline = strdup(line);
					char *_ttype = NULL;

					uint64_t _num = 0;
					int err=0;
					for(i=0; i < strlen(_tmpline); i++)
					{
						if (_tmpline[i] == ',')
						{
							_tmpline[i] = '\0';
							_ttype =  &_tmpline[i+1];
						}
					}


					if (_tmpline)
					{
						char *ip = NULL;
						uint16_t port = 0;
						uint8_t type = RESTREAM_REMOTE_TYPE_NONE;
						ip = strtok(_tmpline, ":");
						port = atoi(strtok(NULL, ":"));


						protocol_get_tuner_count(ip, port, &sock);

						if (sock)
						{
							sz = get_bytes_from_socket(sock, (uint8_t*)&count, sizeof(count), 100, &err);
							if (sz != sizeof(count))
							{
								// Error
							}
							close(sock);
						}
						sock = 0;

						if (_ttype)
							type = atoi(_ttype);

						utils_log_print(dev->device->log, "[RESTREAM] Getting tuner/channel data from: %s (%d) -- Count: %d (type: %d)\n", ip, port, count, type);


						_restream_chan_group_add(dev, line, (uint8_t) count, type);
						protocol_get_channel_list(ip, port, &sock);

						if (sock)
						{
							PROTOCOL_CHANNEL_STRUCT p_channel;

							memset(&p_channel, 0, sizeof(PROTOCOL_CHANNEL_STRUCT));
							while(p_channel.is_last == 0)
							{
								sz = get_bytes_from_socket(sock, (uint8_t*)&p_channel, sizeof(PROTOCOL_CHANNEL_STRUCT), 1000, &err);
								if (sz != sizeof(PROTOCOL_CHANNEL_STRUCT))
								{
									// Error
								}

								_num = p_channel.num;
								device_channel_add(dev->device, _num, _num);
								device_channel_add_name(dev->device, _num, _num, p_channel.name);
								device_channel_add_url(dev->device, _num, _num, line);
							}

							close(sock);
						}
						free(_tmpline);
					}
				}
			}
			utils_log_print(dev->device->log, "[RESTREAM] .. Done\n");

			free(memf);
		}

		free(tmp_mem);
	}

	return 0;
}

int restream_tune_channel(TUNER_STRUCT *tuner, TUNER_INITPASS_STRUCT *tpass)
{
	int ret = TUNER_TUNE_ERROR_UNKNOWN, err = 0;
//	printf("restream_tune_channel Start: %d\n", chan);
	if (tuner && tuner->parent && tpass)
	{
		uint32_t chan = tpass->channel_num;
		TUNER_CHANNEL_STRUCT * tchan = NULL;
		DEVICE_STRUCT *dev = (DEVICE_STRUCT *)tuner->parent;

		if (dev)
		{
			tchan = device_channel_get_struct(dev, chan, 0);
			if (tchan)
			{
				PROTOCOL_TUNE_STRUCT * _ptmp = (PROTOCOL_TUNE_STRUCT *)tpass->dataptr;

				if (_ptmp)
				{
//					printf("HAD ptmp\n");
					tchan->prot_params = (void*)_ptmp;
				}
				else if (tpass->url_params)
				{
//					printf("HAD url: %s\n", tpass->url_params);
					tchan->url_params = tpass->url_params;
				}
				else
				{
//					printf("NO _rtmp!!!!!!\n");
				}

//				tuner->devhandle = _restream_http_connect(tuner, tchan);
				tuner->devhandle = _restream_http_connect(tchan, dev->parent);
				if (tuner->devhandle > 2)
				{
					int i;
					uint8_t data[TS_PACKET_SIZE] = {0};
					size_t bytes_read;

					tuner->tuned_channel = 0;
					ret = TUNER_TUNE_ERROR_NOCHAN;
					for(i=0; i < 2; i++)
					{
						memset(data, 0, TS_PACKET_SIZE);
						bytes_read = get_bytes_from_socket(tuner->devhandle, data, TS_PACKET_SIZE, 6000, &err);
						if (!(bytes_read == TS_PACKET_SIZE && data[0] == 0x47))
						{
							break;
						}
					}

					if (i == 2)
					{
						tuner->tuned_channel = chan;
						printf("[RESTREAM] Tuned Good: %" PRId64" (time: %" PRId64")\n", tuner->tuned_channel, (uint64_t)time(NULL));
						ret = 0;
					}
					else
						printf("[RESTREAM] Tuned Bad\n");

				}
			}
			else
			{
				ret = TUNER_TUNE_ERROR_NOCHAN;
			}
		}
	}
	else
	{
//		printf("restream_tune_channel NOOOOOO\n");
	}
//	printf("restream_tune_channel end\n");
	return ret;
}

uint8_t restream_close_data(TUNER_STRUCT * tuner)
{
	printf("restream_close_data\n");
	uint8_t ret = 1;
	if (tuner)
	{
		if(tuner->devhandle > 2)
		{
//			DEVICE_STRUCT *dev = (DEVICE_STRUCT *)tuner->parent;
//			utils_log_print(dev->log, "[RESTREAM] closing socket: %d\n", tuner->devhandle);
			close(tuner->devhandle);
			tuner->devhandle = 0;
		}
	}
	return ret;
}

uint8_t restream_destroy(TUNER_STRUCT * tuner)
{
	TUNER_STRUCT * del = NULL;
	DEVICE_STRUCT *dev = (DEVICE_STRUCT *)tuner->parent;

	utils_log_print(dev->log, "[RESTREAM] Exiting, cleaning up...\n");
	while(tuner)
	{
		device_tuner_destroy(tuner);
		del = tuner;
		tuner = tuner->next;

		if (del)
		{
			mpegts_io_close(del->mpegts_io, 1);
			//free(del);
		}

	}
	device_destroy(dev);
	return 0;
}

// Ret 1 = Bad
// Ret 0 = Good

uint8_t restream_read_data(TUNER_STRUCT * tuner)
{
	uint8_t ret = DEVICE_READ_ERROR;
//	printf("restream_read_data\n");
	if (tuner && tuner->read_data_ptr)
	{
		DEVICE_STRUCT *dev = tuner->parent;
		int err = 0;
		size_t bytes_read = 0, offset = 0;
		size_t read_sz = tuner->read_size;
		uint64_t time_start = 0, read_timeout = 1000;
		uint64_t time_now = 0;

		TUNER_RESTREAM_STRUCT * restream_dev = dev->parent;
		TUNER_CHANNEL_STRUCT * tuner_channel = device_channel_get_struct(dev, tuner->tuned_channel,  tuner->tuned_channel);
		RESTREAM_CHANNEL_GROUP * group = NULL;

		if (restream_dev)
			group = _restream_chan_group_get_struct(restream_dev, tuner_channel->urls[0]);

		if (group)
		{
			if (group->type == RESTREAM_REMOTE_TYPE_HLS)
				read_timeout = 10000;
//			printf("[RESTREAM] restream_read_data type: %d\n", group->type);
		}


		if (!tuner->haz_data)
		{
			int i;
			bytes_read = get_bytes_from_socket(tuner->devhandle, tuner->read_data_ptr, TS_PACKET_SIZE, read_timeout, &err);
//			printf("read_data: 0x%x 0x%x 0x%x 0x%x\n", tuner->read_data_ptr[0], tuner->read_data_ptr[1], tuner->read_data_ptr[2], tuner->read_data_ptr[3]);
			for (i=0; i < TS_PACKET_SIZE; i++)
				if (tuner->read_data_ptr[i] != SPECIAL_NULL_PACKET[i])
					break;
			if (i >= TS_PACKET_SIZE)
			{
				tuner->fix_stream = 0;
//				utils_log_print(dev->log, "[RESTREAM] Talking to a sibling, turn off PAT/PMT & timestamp fixing\n");
			}

		}
		tuner->haz_data = 1;

		time_start = get_time_ms();

//		printf("restream_read_data 1\n");

		do
		{
			bytes_read = get_bytes_from_socket(tuner->devhandle, tuner->read_data_ptr + offset, read_sz, read_timeout, &err);

			if ((int64_t)bytes_read < 0)
			{
				utils_log_print(dev->log, "[RESTREAM] restream_read_data (%d), get_bytes err: %s (%d) (%" PRId64")\n", tuner->devhandle, strerror(errno), errno, (int64_t)bytes_read);
				break;
			}
			else if (bytes_read == 0)
			{
				usleep(tuner->read_thread_sleep_time);
				time_now = get_time_ms();
				if (time_now - time_start > 250)
//				if (time_now - time_start > 100)
				{
					if (dev && dev->parent)
					{
						if (tuner_channel)
						{
							if (group)
							{
								if (group->type == RESTREAM_REMOTE_TYPE_HLS)
								{
									printf("[RESTREAM] restream_read_data, timed out 1a-hls: %" PRId64"\n", bytes_read);
									if (time_now - time_start < 1500)
										continue;
									printf("[RESTREAM] restream_read_data, timed out 1b-hls\n");
//									return DEVICE_READ_NODATA;
									return DEVICE_READ_ERROR;
								}
								else
								{
									printf("[RESTREAM] restream_read_data, timed out, type: %d\n", group->type);
								}
							}
							else
							{
								printf("[RESTREAM] restream_read_data, timed out 2, no type\n");
							}
						}
					}
					break;
				}

				continue;
			}

			if (read_sz >= bytes_read)
			{
				read_sz -= bytes_read;
				offset += bytes_read;
			}
			else
			{
				utils_log_print(dev->log, "[RESTREAM] restream_read_data error2, read_sz: %" PRId64" -- bytes_read: %" PRId64"\n", read_sz, bytes_read);
				break;
			}
		}
		while (offset < tuner->read_size);

		if (offset == tuner->read_size)
			ret = 0;
	}
	else
	{
//		printf("[RESTREAM] read_data error, something null\n");
	}
//	printf("restream_read_data end\n");
	return ret;
}


int _restream_tuner_internal_init(TUNER_STRUCT *tmptuner, DEVICE_INIT_ARGS_STRUCT *devopts)
{
	if (tmptuner && devopts)
	{
		tmptuner->read_thread_sleep_time = 1000;
		tmptuner->read_block_time = 128 * 1000;
//		tmptuner->read_block_time = 0;
		tmptuner->mpegts_fileunit_maxsize = devopts->max_segment_size;
		tmptuner->mpegts_fileunit_maxlife = devopts->max_segment_life;

		if (devopts->max_cachesize)
		{
			tmptuner->mpegts_fileunit_cachesize = devopts->max_cachesize;
		}
		else
		{
			tmptuner->mpegts_fileunit_cachesize = TS_PACKET_SIZE * 532;
			//tmptuner->mpegts_fileunit_cachesize = 0;
		}
//		tmptuner->mpegts_fileunit_cachesize = 0;
//		printf("[RESTREAM] file cache size: %d\n", tmptuner->mpegts_fileunit_cachesize);
		tmptuner->segment_dir = devopts->segment_dir;
		tmptuner->base_filename = devopts->base_filename;

		tmptuner->cb_read_data = restream_read_data;
		tmptuner->cb_close_data = restream_close_data;
		tmptuner->cb_tune_channel = restream_tune_channel;

//		tmptuner->packets_to_buffer = RESTREAM_DEFAULT_READ_SZ/(2*188);
		tmptuner->packets_to_buffer = 10;
		tmptuner->read_size = RESTREAM_DEFAULT_READ_SZ;
	}
	return 0;
}

TUNER_RESTREAM_STRUCT * tuner_restream_init(DEVICE_INIT_ARGS_STRUCT *devopts)
{
	TUNER_RESTREAM_STRUCT * restream_dev = NULL;

	if (devopts && devopts->channels_file && devopts->bind_ip && devopts->bind_port)
	{
		restream_dev = calloc(1, sizeof(TUNER_RESTREAM_STRUCT));
		if (restream_dev)
		{
			int i;
			restream_dev->channels_file = strdup(devopts->channels_file);
			restream_dev->device = device_init(devopts->bind_ip, devopts->bind_port);
			restream_dev->xmltv_display_index = 3;

			_restream_get_channel_lineup(restream_dev);
//			device_channel_print_all(restream_dev->device);
			_restream_chan_group_print_all(restream_dev);

			if (restream_dev->device)
			{
				TIMER_ARG_STRUCT timeropts = {0};
				TUNER_STRUCT *tmptuner = NULL;
				char tmpdev[MAX_DEVNAME_SIZE] = {0};
				restream_dev->device->cb_wait = _restream_dev_wait;

				restream_dev->device->cb_dev_printf = generic_vprintw;
				restream_dev->device->max_tuners = devopts->max_tuners;

				pthread_mutex_lock(&restream_dev->device->tunerlock);
				restream_dev->device->parent = (void *) restream_dev;

				for (i=0; i < restream_dev->device->max_tuners; i++)
				{
					tmptuner = device_tuner_init(TUNE_TYPE_RESTREAM);

					if (tmptuner)
					{
						_restream_tuner_internal_init(tmptuner, devopts);

						tmptuner->parent = (void*) restream_dev->device;
						snprintf(tmpdev, MAX_DEVNAME_SIZE, "ReStream Tuner-%02d", i);
						strncpy(tmptuner->name, tmpdev, strlen(tmpdev));

						tmptuner->cb_gui_tuner_read_status  = win_tuner_read_status;
						tmptuner->cb_gui_tuner_write_status  = win_tuner_write_status;

						tmptuner->id_offset =  1;
						tmptuner->id = i+1;

						device_tuner_add(restream_dev->device, tmptuner);
						pthread_mutex_unlock(&tmptuner->init_mutex);
					}
					else
					{
//						printf("BLAAAAAAAAAA\n");
					}
				}


				// Timer stuff
//				timeropts.record_dir = calloc(1, MAX_FILENAME_SIZE);

				timeropts.pre_pad = devopts->timer_pre_pad;
				timeropts.post_pad = devopts->timer_post_pad;
				timeropts.record_dir = calloc(1, MAX_FILENAME_SIZE);

				if (devopts->segment_dir[strlen(devopts->segment_dir)-1] == '/')
				{
					devopts->segment_dir[strlen(devopts->segment_dir)-1] = 0;
				}

				if (devopts->recording_dir)
				{
					timeropts.record_dir = strdup(devopts->recording_dir);
				}
				else
				{
					timeropts.record_dir = calloc(1, MAX_FILENAME_SIZE);
					sprintf(timeropts.record_dir, "%s/recordings", devopts->segment_dir);
				}
				restream_dev->recording_dir = strdup(timeropts.record_dir);
				mkdir(timeropts.record_dir, S_IRWXU | S_IRWXG | S_IRWXO);

				timeropts.state_filename = calloc(1, MAX_FILENAME_SIZE);
				timeropts.extra_data = (void*)restream_dev;
				strncat(timeropts.state_filename, devopts->segment_dir, MAX_FILENAME_SIZE-1);
//				printf("HMM: %c\n", devopts->segment_dir[strlen(devopts->segment_dir)-1]);
				if (devopts->segment_dir[strlen(devopts->segment_dir)-1] != '/')
					strncat(timeropts.state_filename, "/", MAX_FILENAME_SIZE-1);
				strncat(timeropts.state_filename, devopts->timer_file, MAX_FILENAME_SIZE-1);

				utils_log_print(restream_dev->device->log, "[RESTREAM] -- Recordings dir: %s\n", timeropts.record_dir);
				utils_log_print(restream_dev->device->log, "[RESTREAM] -- State File: %s\n", timeropts.state_filename);
//				printf("[RESTREAM] -- xmltv file: %s\n", timeropts.xmltv_filename);
				restream_dev->timer_ctl = timers_init(&timeropts);
//				restream_dev->timer_ctl->cb_timer_change_state = restream_timer_change_state;

				restream_dev->device->cb_timer_add = _restream_timer_add;
				restream_dev->device->cb_timer_del = _restream_timer_del;
				restream_dev->device->cb_timer_list = _restream_timer_list;
				restream_dev->device->cb_timer_count = _restream_timer_count;
				restream_dev->device->cb_timer_seqnum = _restream_timer_seqnum;

				restream_dev->device->cb_rec_count = _restream_rec_count;
				restream_dev->device->cb_rec_list = _restream_rec_list;
				restream_dev->device->cb_rec_play = _restream_rec_play;
				restream_dev->device->cb_rec_del = _restream_rec_del;
				restream_dev->device->cb_rec_stop = _restream_rec_stop;
				restream_dev->device->cb_rec_seqnum = _restream_rec_seqnum;

				restream_dev->device->cb_epg_chan = _restream_get_chan_epg;

				if (devopts->filter_type && devopts->filter_file)
				{
//					restream_dev->device->channel_filter = calloc(1, sizeof(TUNER_CHANNEL_FILTER_STRUCT));
					restream_dev->device->channel_filter->type = devopts->filter_type;
					restream_dev->device->channel_filter->filename = strdup(devopts->filter_file);
					device_reload_channel_filter(restream_dev->device);
//					_restream_get_channel_names_from_xmltv(restream_dev);
				}

				if (devopts->zap_lineup || devopts->zap_zip)
				{
					if (devopts->zap_lineup && devopts->zap_zip)
					{
						restream_dev->zap2it_lineup = strdup(devopts->zap_lineup);
						restream_dev->zap2it_zipcode = strdup(devopts->zap_zip);
						restream_dev->zap2it_cachedir = "./zap_cache";
						_restream_get_zap2it(restream_dev);
					}
					else
					{
						if (!devopts->zap_lineup)
							printf("Must specify lineup, ex: -Z USA-WA62449-X\n");

						if (!devopts->zap_zip)
							printf("Must specify zipcode, ex: -z 98033\n");

						return NULL;
					}
				}
				else if (devopts->xmltv_file)
				{
					restream_dev->xmltv_file = strdup(devopts->xmltv_file);
//					utils_log_print(restream_dev->device->log, "[RESTREAM] xmltv file: %s\n", restream_dev->xmltv_file);
					_restream_get_channel_names_from_xmltv(restream_dev);
				}

				restream_dev->device->cb_reload_epg = _restream_reload_epg;
				_restream_reload_recordings(restream_dev);

				pthread_mutex_unlock(&restream_dev->device->tunerlock);
			}

			printf("[RESTREAM] Waiting for zap to finish loading..\n");

			if (restream_dev->zap2it_lineup)
			{
				while(restream_dev->zap2it_epg->last_update_time == 0)
				{
//					printf(".");
					usleep(1000*500);
				}
			}
			restream_dev->init_done = 1;
			printf("[RESTREAM] Init done\n");
		}
	}
	return restream_dev;
}


#ifdef _MAIN_
uint8_t sigint_h = 0;
uint8_t	use_curses = 1;


TUNER_RESTREAM_STRUCT * restream_device = NULL;

DEVICE_INIT_ARGS_STRUCT _opts = {	.bind_ip = RESTREAM_DEFAULT_BINDIP,
									.bind_port = RESTREAM_DEFAULT_BINDPORT,
									.max_segment_size = MPEGTS_IO_MAX_SIZE_DEFAULT,
									.channels_file = RESTREAM_DEFAULT_CHANNELS_CONF,
									.max_tuners = RESTREAM_MAX_DEV_COUNT,
									.timer_file = RESTREAM_TIMER_FILENAME,
									.timer_post_pad = RESTREAM_DEFAULT_PAD_TIME,
									.timer_pre_pad = RESTREAM_DEFAULT_PAD_TIME,
								};


void display_usage(char *exe)
{
//	fprintf(stderr,"bleah\n");
}


static const char *optString = "Z:z:R:P:1:2:3:s:t:f:F:l:p:m:r:c:O:o:x:n?h";

static const struct option longOpts[] =
{
	{ "help", no_argument, NULL, 'h' },
	{ NULL, no_argument, NULL, 0 }
};

uint8_t get_opts(int argc, char *const argv[], DEVICE_INIT_ARGS_STRUCT * devopts)
{
	int index = 0;
	int opt = getopt_long( argc, argv, optString, longOpts, &index );

	if (opt == -1 )
	{
		display_usage(argv[0]);
		return 1;
	}

	while ( opt != -1 )
	{
		switch ( opt )
		{
			case '1':
				devopts->max_segment_size = atoi(optarg);
				break;

			case '2':
				devopts->max_segment_life = atoi(optarg);
				break;

			case '3':
				devopts->max_cachesize = atoi(optarg);
				break;

			case 'l':
				devopts->bind_ip = strdup(optarg);
				break;

			case 'p':
				devopts->bind_port = atoi(optarg);
				break;

			case 'O':
				devopts->timer_pre_pad = atoi(optarg);
				break;

			case 'o':
				devopts->timer_post_pad = atoi(optarg);
				break;

			case 'n':
				use_curses = !use_curses;
				break;

			case 'R':
				break;

			case 'P':
				break;

			case 'c':
				devopts->channels_file = strdup(optarg);
				break;

			case 'z':
				devopts->zap_zip = strdup(optarg);
				break;

			case 'Z':
				devopts->zap_lineup = strdup(optarg);
				break;

			case 'm':
				devopts->max_tuners = atoi(optarg);
				break;

			case 's':
				devopts->segment_dir = strdup(optarg);
				break;

			case 'r':
				devopts->recording_dir = strdup(optarg);
				break;

			case 't':
				devopts->timer_file = strdup(optarg);
				break;

			case 'f':
				devopts->base_filename = strdup(optarg);
				break;

			case 'F':
				if (optarg)
				{
					devopts->filter_type = TUNER_CHANNEL_FILTER_INCLUDE;
					if (optarg[1] == ':')
					{
						if (optarg[0] == 'e')
							devopts->filter_type = TUNER_CHANNEL_FILTER_EXCLUDE;
						optarg = optarg + 2;
					}
					devopts->filter_file = strdup(optarg);

					printf("Filter Type: 0x%x, File: %s\n", devopts->filter_type, devopts->filter_file);
				}
				break;

			case 'x':
				devopts->xmltv_file = strdup(optarg);
				break;

			case 'h':
			case '?':
				display_usage(argv[0]);
				break;

			default:
				break;
		}
		opt = getopt_long( argc, argv, optString, longOpts, &index );
	}
	return 0;
}

void sigint_handler()
{
	if (!sigint_h)
	{
		if (!use_curses)
		{
			pthread_mutex_unlock(&restream_device->init_mutex);
		}
		sigint_h = 1;
	}
}

void sigpipe_handler()
{
//   printf("SIGPIPE caught\n");
}

void sigusr1_handler()
{
	printf("SIGUSR1 caught\n");
}


int main(int argc, char *argv[])
{
//	char tmp[8096] = {0};
	signal(SIGPIPE, sigpipe_handler);
	signal(SIGINT, sigint_handler);
	signal(SIGUSR1, sigusr1_handler);

//	printf("---------- %s\n", argv[0]);
	get_opts(argc, argv, &_opts);

	_opts.max_segment_size = get_nearest_page_size(_opts.max_segment_size, TS_PACKET_SIZE);

	restream_device = tuner_restream_init(&_opts);
	if (restream_device && restream_device->device)
	{
		if (use_curses)
		{
			int loop = 1, ch;
//			win_init();
			restream_device->screen = edpvr_win_init(restream_device->device);

			while (loop && !sigint_h)
			{
				ch = getch();
				switch (ch)
				{
					case 'q' :
					case 'Q' :
						loop = 0;
					break;

					case ERR:
					{
						char *msg = NULL;
						do
						{
							msg = utils_log_getmsg(restream_device->device->log);
							if (msg)
							{
								win_screen_printw(restream_device->screen, msg);
								free(msg);
							}
						} while (msg);
						usleep(1000*100);
						win_screen_update_all(restream_device->screen);
					}
					break;

					default:
					break;
				}
			}
		}
		else
		{
			int64_t last_trim = 0, now;
			char *msg = NULL;
			while(!sigint_h)
			{
				now = time(NULL);
				if (now - last_trim > 5)
				{
//					printf("Running trim\n");
					malloc_trim(0);
					last_trim = now;
				}
				do
				{
					msg = utils_log_getmsg(restream_device->device->log);
					if (msg)
					{
//						win_screen_printw(restream_device->screen, msg);
						printf("%s", msg);
						free(msg);
					}
				} while (msg);
				usleep(1000);
			}
		}

		if (restream_device && restream_device->device && restream_device->device->first_tuner)
			restream_destroy(restream_device->device->first_tuner);
		restream_device->timer_ctl->die = 1;

//		usleep(1000* 2000);
		if (use_curses)
		{
			win_destroy_screen(restream_device->screen);
		}
		else
		{
		}
/*
*/
	}
	else
	{
		printf("Something bad\n");
		return 1;
	}
	while(restream_device->timer_ctl && restream_device->timer_ctl->timer_thread)
	{
		printf("[RESTREAM] Waiting for timer thread to die\n");
		usleep(1000*1000);
	}

	printf("[RESTREAM] App exiting\n");
	return 0;
}
#endif
